const gulp = require('gulp');
const fs = require('fs');
const del = require('del');
const connect = require('gulp-connect');
const less = require('gulp-less');
const sass = require('gulp-sass');
const stylus = require('gulp-stylus');
const autoprefix = require('gulp-autoprefixer');
const cssnano = require('gulp-cssnano');
const babel = require('gulp-babel');
const uglify = require('gulp-uglify');
const tinify = require("tinify");
const htmlmini = require('gulp-htmlmin');
const rev = require('gulp-rev');
const revCollector = require('gulp-rev-collector');
const concat = require('gulp-concat');
const remember = require('gulp-remember');
const promisify = require('cb-promisify');

const workplace = './src/';
const dist = './dist/';
const temp = './temp/'
const tinypngkey = '6kmD6K6fzqZ5czyV6g24FDYHYrS6Xrkf';// 熊猫压缩key

// utilutilutilutilutilutilutilutilutilutilutilutilutilutilutil
const gulpWPSrc = (path, opts = {}) => gulp.src(workplace + path, Object.assign({ allowEmpty: true }, opts));
const gulpDSrc = (path, opts = {}) => gulp.src(dist + path, Object.assign({ allowEmpty: true }, opts));
const gulpTSrc = (path, opts = {}) => gulp.src(temp + path, Object.assign({ allowEmpty: true }, opts));
const tinifyValidatePro = promisify(tinify.validate, tinify);
const fsReaddirPro = promisify(fs.readdir);
const fsReadFilePro = promisify(fs.readFile);
const fsWriteFilePro = promisify(fs.writeFile);
// utilutilutilutilutilutilutilutilutilutilutilutilutilutilutil


const htmlMiniOptions = {
  removeComments: true,// 清除HTML注释
  collapseWhitespace: true,// 压缩HTML
  collapseBooleanAttributes: false,// 省略布尔属性的值 <input checked="true"/> ==> <input />
  removeEmptyAttributes: false,// 删除所有空格作属性值 <input id="" /> ==> <input />
  removeScriptTypeAttributes: true,// 删除<script>的type="text/javascript"
  removeStyleLinkTypeAttributes: true,// 删除<style>和<link>的type="text/css"
  minifyJS: true,// 压缩页面JS
  minifyCSS: true// 压缩页面CSS
}

/** 启动一个server */
const startServerTask = () => {

  connect.server({
    port: 9090,
    root: dist,
    livereload: true,
  });
}

/** index.html热更新、 */
const reloadServerTask = () => {

  return gulp.src(['./temp/*.json', workplace + 'index.html'])
  .pipe(revCollector())// { replaceReved:true }
  .pipe(htmlmini(htmlMiniOptions))
  .pipe(gulp.dest(dist))
  .pipe(connect.reload());
}

/**
 * # css编译
 * - 支持less、scss、stylus
 * - autoprefix
 * - 压缩代码
 * - 合并为一个文件
 * - hash版本号
 */
const cssCompileTask = () => {

  const preProcessorsHandler = (pp, type) => new Promise((resolve, reject) => {

    del([dist + 'css/**'])
    .then(() => {
      pp.pipe(remember(type + '-cache'))
      .pipe(concat(type + '.css'))
      .pipe(gulp.dest(temp + 'css'))
      .on('end', () => resolve());
    });
  });

  const lessHandler = preProcessorsHandler(gulpWPSrc('css/*.less').pipe(less(), { since: gulp.lastRun(cssCompileTask) }), 'less');
  const scssHandler = preProcessorsHandler(gulpWPSrc('css/*.scss').pipe(sass(), { since: gulp.lastRun(cssCompileTask) }), 'scss');
  const stylusHandler = preProcessorsHandler(gulpWPSrc('css/*.styl').pipe(stylus(), { since: gulp.lastRun(cssCompileTask) }), 'stylus');
  const cssHandler = preProcessorsHandler(gulpWPSrc('css/*.css'), 'css');

  return new Promise((resolve, reject) => {
    Promise.all([lessHandler, scssHandler, stylusHandler, cssHandler])
    .then(() => {

      gulpTSrc('css/*.css')
      .pipe(concat('app.css'))
      .pipe(autoprefix())
      .pipe(cssnano())
      .pipe(rev())
      .pipe(gulp.dest(dist + 'css'))
      .pipe(rev.manifest('rev-css-manifest.json'))
      .pipe(gulp.dest('./temp'))
      .on('end', () => resolve());
    });
  });

}

/**
 * # babel转码
 * - 仅支持es6语法转码
 * - 不支持新api转码
 * - hash版本号
 */
const babelTask = () => {

  return new Promise((resolve, reject) => {

    del([dist + 'js/*'])
    .then(() => {
      gulpWPSrc('js/*.js', { since: gulp.lastRun(babelTask) })
      .pipe(remember('js-cache'))
      .pipe(concat('app.js'))
      .pipe(babel().on('error', e => console.log('babel error：', e)))
      .pipe(uglify())
      .pipe(rev())
      .pipe(gulp.dest(dist + 'js'))
      .pipe(rev.manifest('rev-js-manifest.json'))
      .pipe(gulp.dest('./temp'))
      .on('end', () => resolve());
    });
  });
}

/**
 * # tiny熊猫压缩
 * TODO:
 * - [x] 优化回调地狱
 * - 文件压缩前后大小显示
 */
const imageMiniTask = async () => {

  let imgsLen = 0;
  let minied = 0;
  const tinifyCount = {
    used: 0,
    total: 500,
  }

  tinify.key = tinypngkey;
  await tinifyValidatePro(); 
  tinifyCount.used = tinify.compressionCount;

  const [err0, files] = await fsReaddirPro(workplace + 'images/');
  imgsLen = files.length;

  console.log('本次压缩：' + imgsLen, '，本月剩余：' + (500 - +tinifyCount.used) + '/' + 500);

  // 保证images目录存在
  if(!fs.statSync(dist + 'images').isDirectory()) fs.mkdirSync(dist + 'images');

  files.forEach(async file => {
    const [err1, sourceData] = await fsReadFilePro(workplace + 'images/' + file);
    tinify.fromBuffer(sourceData).toBuffer(async (err, resultData) => {
      const [err2] = await fsWriteFilePro(dist + 'images/' + file, resultData);
      console.log('成功压缩并写入：' + ++minied + '/' + imgsLen);
    });
  });
}

/**
 * # 给图片添加版本号
 */
const revImages = () => {

  return gulp.src(dist + 'images/*.{png,jpg,gif}')
  .pipe(rev())
  .pipe(gulp.dest(dist + 'images'))
  .pipe(rev.manifest('rev-img-manifest.json'))
  .pipe(gulp.dest('./temp'));
}

/**
 * # 直接复制文件
 */
const copyStaticTask = () => {

  return gulpWPSrc('static/*.*')
  .pipe(gulp.dest(dist + 'static'));
}

/**
 * @method 执行并监听
 */
const watchTask = () => {

  cssCompileTask();
  gulp.watch(workplace + 'css/*.{css,less,scss,stly}', gulp.series(cssCompileTask, reloadServerTask));

  babelTask();
  gulp.watch(workplace + 'js/*.js', gulp.series(babelTask, reloadServerTask));

  startServerTask();
  reloadServerTask();
  gulp.watch(workplace + 'index.html', reloadServerTask);
}

const defaultTask = async () => {
  watchTask();
}

exports.default = defaultTask;
exports.imageMini = imageMiniTask;
exports.copyStatic = copyStaticTask;